#include "luainterface.h"

#include "gamestate.h"
#include "luacallbacks.h"

#include "entities/city.h"

LuaInterface lua;

void LuaInterface::Init(){
    L = lua_open();
    luaL_openlibs(L);
    lua_register(L, "GetPopulation", GetPopulation);
    lua_register(L, "GetCityOwner", GetCityOwner);
    lua_register(L, "GetOwner", GetOwner);
    lua_register(L, "AddIntVar", AddIntVar);
    lua_register(L, "SetIntVar", SetIntVar);
    lua_register(L, "GetIntVar", GetIntVar);
    lua_register(L, "GetOwner", GetOwner);
    lua_register(L, "GetIndex", GetIndex);
    lua_register(L, "DeleteImprovement", DeleteImprovement);
    lua_register(L, "CreateCity", CreateCity);
    lua_register(L, "DeleteUnit", DeleteUnit);
    lua_register(L, "AddConstruction", AddConstructionOption);
    lua_register(L, "GetTileInfo", GetTileInfo);
    lua_register(L, "GetUnitInfo", GetUnitInfo);
    lua_register(L, "GarrisonArmy", GarrisonArmy);
    lua_register(L, "SetMaxFarmDistance", SetMaxFarmDistance);
    lua_register(L, "GetContainingEntity", GetContainingEntity);
}

bool LuaInterface::LoadValues(std::string filename){
    if(luaL_loadfile(L, filename.c_str())!=0){
        std::cout<<"lua error: "<<lua_tostring(L, -1)<<std::endl;
        return 0;
    }
    if(lua_pcall(L, 0, 1, 0)!=0){
        std::cout<<"lua error: "<<lua_tostring(L, -1)<<std::endl;
        return 0;
    }
    // After now the definition file should have returned a table containing the variables
    if(!lua_istable(L, -1)){
        std::cout<<"LuaInterface::LoadValues Error: Table not loaded to stack from "<<filename<<std::endl;
        return 0;
    }

//    std::string val;
//    lua_pushstring(L, "name");
//    // Stack : 0, "name", Defn
//    lua_gettable(L, -2);
//    if(lua_isstring(L, -1)){
//        val = lua_tostring(L, -1);
//        std::cout<<val<<std::endl;
//    }
//    else{
//        std::cout<<"LuaInterface::LoadValues Error reading string: "<<"name"<<std::endl;
//    }
//    lua_pop(L, 1);

    return -1;
}

void LuaInterface::LoadScript(const char *filename){
    luaL_dofile(L, filename);
}
int LuaInterface::GetFunctionReference(const char *function){
    lua_getglobal(L, function);
    return luaL_ref(L,LUA_REGISTRYINDEX);
}
void LuaInterface::PrepFunction(int ref){
    lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
}
void LuaInterface::AddArgument(int arg){
    lua_pushnumber(L, arg);
}
void LuaInterface::CallFunction(){
    lua_pcall(L, 1, 1, 0);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int LuaInterface::GetInt(const char* var){
    int val;
    lua_pushstring(L, var);
    lua_gettable(L, -2);   // This pops var, gets the value of var in the table and pushes the value
    if(lua_isnumber(L, -1)){
       val = (int)lua_tonumber(L, -1);  // Get the returned value off the stack
    }
    else{
//        std::cout<<"Error reading int: "<<var<<std::endl;
        val = 0;
    }
    lua_pop(L, 1);   // Pops that value off the stack (returning stack to its original state)
    return val;
}

float LuaInterface::GetFloat(const char *var){
    float val;
    lua_pushstring(L, var);
    lua_gettable(L, -2);
    if(lua_isnumber(L, -1)){
        val = (float)lua_tonumber(L, -1);
    }
    else{
//        std::cout<<"Error reading float: "<<var<<std::endl;
        val = 0;
    }
    lua_pop(L, 1);
    return val;
}

std::string LuaInterface::GetString(const char* var){
    std::string val;
    lua_pushstring(L, var);
    lua_gettable(L, -2);
    if(lua_isstring(L, -1)){
        val = lua_tostring(L, -1);
    }
    else{
//        std::cout<<"Error reading string: "<<var<<std::endl;
        val = std::string("none");
    }
    lua_pop(L, 1);
    return val;
}

std::vector<std::string> LuaInterface::GetStringList(const char* var){
    std::vector<std::string> contents;

    lua_pushstring(L, var);
    lua_gettable(L, -2);
    // stack: -1: table "var", -2: table "Defn"
    if(lua_istable(L, -1)){
        lua_pushnil(L);
        // stack: -1: key (nil)
        //        -2: table "var"
        //        -3: table "Defn"

        while(lua_next(L, -2)){
            // stack: -1: value
            //        -2: key
            //        -3: table "var"
            //        -4: table "Defn"
            contents.push_back(lua_tostring(L, -1));
            lua_pop(L, 1);
            // stack: -1: key
            //        -2: table "var"
            //        -3: table "Defn"
        }
    }
    else{
//        std::cout<<"Error reading string list: "<<var<<std::endl;
    }
    lua_pop(L, 1);
    return contents;
}

std::set<std::string> LuaInterface::GetStringSet(const char* var){
    std::set<std::string> contents;

    lua_pushstring(L, var);
    lua_gettable(L, -2);
    // stack: -1: table "var", -2: table "Defn"
    if(lua_istable(L, -1)){
        lua_pushnil(L);
        // stack: -1: key (nil)
        //        -2: table "var"
        //        -3: table "Defn"

        while(lua_next(L, -2)){
            // stack: -1: value
            //        -2: key
            //        -3: table "var"
            //        -4: table "Defn"
            contents.insert(lua_tostring(L, -1));
            lua_pop(L, 1);
            // stack: -1: key
            //        -2: table "var"
            //        -3: table "Defn"
        }
    }
    else{
//        std::cout<<"Error reading string list: "<<var<<std::endl;
    }
    lua_pop(L, 1);
    return contents;
}

std::vector<float> LuaInterface::GetFloatList(const char* var){
    std::vector<float> contents;

    lua_pushstring(L, var);
    lua_gettable(L, -2);
    if(lua_istable(L, -1)){
        lua_pushnil(L);
        while(lua_next(L, -2)){
            contents.push_back((float)lua_tonumber(L, -1));
            lua_pop(L, 1);
        }
    }
    else{
//        std::cout<<"Error reading float list: "<<var<<std::endl;
    }
    lua_pop(L, 1);
    return contents;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void LuaInterface::PushInt(int var){
    lua_pushnumber(L, var);
}

void LuaInterface::RegisterCallback(std::string name, int (*f)(...)){
    lua_register(L, name.c_str(), f);
}
